// $Id: array.hpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#ifndef XPCOM_ARRAY_HPP
#define XPCOM_ARRAY_HPP

#include <xpcom/config.hpp>
#include <cstddef>
#include <algorithm>
#include <iterator>
#include <xpcom/types.hpp>
#include <xpcom/array_fwd.hpp>
#include <xpcom/array_traits.hpp>
#include <xpcom/assert.hpp>
#include <xpcom/exception.hpp>
#include <xpcom/detail/array_impl.hpp>
#include <xpcom/detail/array_iterator.hpp>
#include <xpcom/detail/move.hpp>

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif

namespace xpcom {

namespace detail {

template <typename Array>
struct array_access;

} // namespace xpcom::detail

// Base class of XPCOM arrays
template <typename Traits>
class array_base
{
    typedef array_base<Traits> this_type;
protected:
    typedef detail::array_impl<Traits> impl_type;
public:
    typedef Traits                                      traits_type;
    typedef typename traits_type::element_type          element_type;
    typedef typename traits_type::value_type            value_type;

    typedef typename impl_type::size_type               size_type;
    typedef std::ptrdiff_t                              difference_type;
    typedef typename traits_type::pointer               pointer;
    typedef typename traits_type::const_pointer         const_pointer;
    typedef typename traits_type::reference             reference;
    typedef typename traits_type::const_reference       const_reference;

    typedef detail::array_iterator<traits_type>         iterator;
    typedef detail::array_const_iterator<traits_type>   const_iterator;
    typedef std::reverse_iterator<iterator>             reverse_iterator;
    typedef std::reverse_iterator<const_iterator>       const_reverse_iterator;

    array_base() :
        impl_()
    {
    }

    array_base(const array_base& rhs) :
        impl_(rhs.size())
    {
        std::copy(rhs.begin(), rhs.end(), begin());
    }

    array_base(move_from<this_type> rhs) :
        impl_(rhs.source.impl_)
    {
    }

    explicit array_base(size_type n) :
        impl_(n)
    {
    }

    template <typename ForwardIterator>
    array_base(ForwardIterator first, ForwardIterator last) :
        impl_(static_cast<size_type>(std::distance(first, last)))
    {
        std::copy(first, last, begin());
    }

    ~array_base()
    {
    }

    //
    // Iterators
    //
    const_iterator begin() const
    {
        return const_iterator(data());
    }

    iterator begin()
    {
        return iterator(data());
    }

    const_iterator end() const
    {
        return const_iterator(data() + size());
    }

    iterator end()
    {
        return iterator(data() + size());
    }

    const_reverse_iterator rbegin() const
    {
        return const_reverse_iterator(end());
    }

    reverse_iterator rbegin()
    {
        return reverse_iterator(end());
    }

    const_reverse_iterator rend() const
    {
        return const_reverse_iterator(begin());
    }

    reverse_iterator rend()
    {
        return reverse_iterator(begin());
    }

    //
    // Size, capacity, etc
    //
    size_type size() const
    {
        return impl_.length();
    }

    size_type max_size() const
    {
        return (static_cast<size_type>(-1) / sizeof(element_type)) - 1;
    }

    size_type capacity() const
    {
        return impl_.size();
    }

    void reserve(size_type n)
    {
        impl_.reserve(n);
    }

    void resize(size_type n)
    {
        impl_.resize(n);
    }

    void clear()
    {
        resize(0);
    }

    bool empty() const
    {
        return size() == 0;
    }

    //
    // Element access
    //
    const_reference operator[](size_type pos) const
    {
        XPCOM_ASSERT(pos < size());
        return *(begin() + pos);
    }

    reference operator[](size_type pos)
    {
        XPCOM_ASSERT(pos < size());
        return *(begin() + pos);
    }

    const_reference at(size_type pos) const
    {
        range_check(pos);
        return (*this)[pos];
    }

    reference at(size_type pos)
    {
        range_check(pos);
        return (*this)[pos];
    }

    const_reference front() const
    {
        XPCOM_ASSERT(!empty());
        return *begin();
    }

    reference front()
    {
        XPCOM_ASSERT(!empty());
        return *begin();
    }

    const_reference back() const
    {
        XPCOM_ASSERT(!empty());
        return *(end() - 1);
    }

    reference back()
    {
        XPCOM_ASSERT(!empty());
        return *(end() - 1);
    }

    void swap(array_base& rhs)
    {
        impl_.swap(rhs.impl_);
    }

protected:
    element_type* data() const
    {
        return impl_.data();
    }

    void range_check(size_type pos) const
    {
        if (pos >= size())
            throw_out_of_range();
    }

    impl_type impl_;
};

//
// XPCOM array class.
//
// This class is mainly used to talk with XPCOM interfaces which has parameter
// of [array] type.  It has limited interface, and is not recommended to use
// as common container, standard containers are more suit for daily usage.
//
// Client code should use predefined array types which is used by XPCOM to
// go across interface boundaries.  You can make your own Traits type to be
// used with array.  But in the generated interface code, default traits are
// always used. See xpcom/array_traits.hpp for interface of traits type.
//
// Value types of the array should be default constructible and assignable,
// copy constructible is not required.
//
template <typename T, typename Traits>
class array : public array_base<Traits>
{
    typedef array<T, Traits> this_type;
    typedef array_base<Traits> base_type;
    typedef typename base_type::impl_type impl_type;
public:
    typedef typename base_type::traits_type             traits_type;
    typedef typename base_type::element_type            element_type;
    typedef typename base_type::value_type              value_type;

    typedef typename base_type::size_type               size_type;
    typedef typename base_type::difference_type         difference_type;
    typedef typename base_type::pointer                 pointer;
    typedef typename base_type::const_pointer           const_pointer;
    typedef typename base_type::reference               reference;
    typedef typename base_type::const_reference         const_reference;

    typedef typename base_type::iterator                iterator;
    typedef typename base_type::const_iterator          const_iterator;
    typedef typename base_type::reverse_iterator        reverse_iterator;
    typedef typename base_type::const_reverse_iterator  const_reverse_iterator;

    array() :
        base_type()
    {
    }

    array(const array& rhs) :
        base_type(rhs)
    {
    }

    array(move_from<this_type> rhs) :
        base_type(move_from<base_type>(rhs.source))
    {
    }

    explicit array(size_type n) :
        base_type(n)
    {
    }

    template <typename ForwardIterator>
    array(ForwardIterator first, ForwardIterator last) :
        base_type(first, last)
    {
    }

    ~array()
    {
    }

    array& operator=(array rhs)
    {
        swap(rhs);
        return *this;
    }

    array& assign(const array& rhs)
    {
        this_type(rhs).swap(*this);
        return *this;
    }

    template <typename ForwardIterator>
    array& assign(ForwardIterator first, ForwardIterator last)
    {
        this_type(first, last).swap(*this);
        return *this;
    }

    void swap(array& rhs)
    {
        base_type::swap(rhs);
    }

protected:
    using base_type::impl_;

private:
    friend struct detail::array_access<this_type>;
};

// Comparisons
template <typename T, typename Traits>
bool operator==(const array<T, Traits>& lhs, const array<T, Traits>& rhs)
{
    return lhs.size() == rhs.size() &&
            std::equal(lhs.begin(), lhs.end(), rhs.begin());
}

template <typename T, typename Traits>
bool operator<(const array<T, Traits>& lhs, const array<T, Traits>& rhs)
{
    return std::lexicographical_compare(
            lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}

template <typename T, typename Traits>
bool operator!=(const array<T, Traits>& lhs, const array<T, Traits>& rhs)
{
    return !(lhs == rhs);
}

template <typename T, typename Traits>
bool operator>(const array<T, Traits>& lhs, const array<T, Traits>& rhs)
{
    return rhs < lhs;
}

template <typename T, typename Traits>
bool operator<=(const array<T, Traits>& lhs, const array<T, Traits>& rhs)
{
    return !(rhs < lhs);
}

template <typename T, typename Traits>
bool operator>=(const array<T, Traits>& lhs, const array<T, Traits>& rhs)
{
    return !(lhs < rhs);
}

// swap
template <typename T, typename Traits>
inline void swap(array<T, Traits>& lhs, array<T, Traits>& rhs)
{
    lhs.swap(rhs);
}

} // namespace xpcom

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif

#endif // XPCOM_ARRAY_HPP
